"""
Module for game application boilerplate.

WARNING: the application boilerplate below was haphazardly thrown together.

the interesting game specific updates reside in TutorialFrameListener. Method of interest: 
__init__, frameStarted

I tried to use frame rendered queued but wasn't working properly.
"""

import ogre.renderer.OGRE as ogre
import ogre.io.OIS as OIS
import SampleFramework as sf
import SampleFramework as sf

from Sprite import (SpriteManager)
from HDRLogic import HDRLogic
from PhysicsManager import (PhysicsManager)
from SkyManager import SkyManager as SkyManager
from Entity import (EntityManager, Entity)
from CinematicController import CinematicController
from PerlinBuildingGen import PerlinBuildingGen
from WorldController import WorldController
from EventController import (Events, EventController)

import ogre.gui.canvas as canvas



class TutorialFrameListener(ogre.FrameListener, ogre.WindowEventListener, OIS.KeyListener):
    def __init__(self, renderWindow, camera, sceneManager, app):
        ogre.FrameListener.__init__(self)
        ogre.WindowEventListener.__init__(self)
        #sf.FrameListener.__init__(self, renderWindow, camera, True)
       
        self.app = app
        
        OIS.KeyListener.__init__(self)
        
        self.renderWindow = renderWindow
        self.camera = camera
        self.sceneManager = sceneManager
        
        #GUI STUFF
        self.doc = canvas.Document(self.sceneManager, self.app.viewport, "DemoLayout.txt")
        
        
        self.setupInput()
        #self.camNode = camera.parentSceneNode.parentSceneNode
       
            
        self.timer = ogre.Timer()
        
        self.rotate = 0.1
        self.move = 10.0
        self.direction = ogre.Vector3(0, 0, 0)
        
        self.keepRendering = True
        
        ogre.MaterialManager.getSingleton().create("BillboardMatDynamic", ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME)
        
        worldInitialFrame = ogre.Vector3(25.0, 4.0, 0.0)
        
        
        self.eventController = EventController()
        self.cameraNode = self.sceneManager.getRootSceneNode().createChildSceneNode("CameraNode")      
        self.cameraNode.attachObject(self.camera)
        #self.camera.setPosition(worldInitialFrame)
        
        ogre.MeshManager.getSingleton().createPlane("Ground2", ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME
                                                             , ogre.Plane(ogre.Vector3(0.0, 1.0, 0.0), 0.0), 5, 5, 1, 1, True, 1, 0.6, 0.6, ogre.Vector3(0.0, 0.0, -1.0))
        carpetCockpitEnt = self.sceneManager.createEntity("CarpetCockpitEnt", "Ground2")
        cockpitMat = ogre.MaterialManager.getSingleton().getByName("TronNormalMap")
        carpetCockpitEnt.setMaterialName(cockpitMat.getName())
        node = self.cameraNode.createChildSceneNode()
        node.attachObject(carpetCockpitEnt)
        node.setPosition(ogre.Vector3(0.0, -0.6, 0.0))
        self.cameraNode.setPosition(worldInitialFrame)
        self.cinematicController = CinematicController(self.camera, self.cameraNode, self.eventController)
        
        self.worldController = WorldController(self.eventController, self.cinematicController, self.sceneManager)
        """
        #build lights
        for i in range(0, 5):
            zLayer = ogre.Vector3(0.0, 0.0, -(float(i)*25.0) )
            light = self.sceneManager.createLight("Light" + str(i))
            #light.type = ogre.Light.LT_DIRECTIONAL
            dir = ogre.Vector3(1.0, -1.0, 0.2)
            dir.normalise()
            light.type = ogre.Light.LT_POINT
            #light.position = 250, 1000, 0
            light.direction = dir
            light.position = worldInitialFrame + zLayer
            light.diffuseColour = 1, 0, 0
            light.specularColour = 1, 0, 0
            light.setCastShadows(False)
        """
        #PROCEDURAL LEVEL
        manMesh = self.sceneManager.createManualObject("Buildings")
        perlin = PerlinBuildingGen(0.0)
        perlin.generateSpline(worldInitialFrame)
        perlin.generate(manMesh)
        
        
        mesh = manMesh.convertToMesh("buildingMesh")
        mesh.setAutoBuildEdgeLists(True)
        mesh.buildEdgeList()
        buildingEnt = self.sceneManager.createEntity(mesh.getName())
        buildingEnt.setCastShadows(True)
        
        
        #SETUP SUBSYSTEMS
        self.spriteManager = SpriteManager(self.sceneManager, self.sceneManager.getRootSceneNode().createChildSceneNode())
        self.phyManager = PhysicsManager(self.sceneManager)
        #self.phyManager.createWorldRep()
        
        self.entityManager = EntityManager(self.spriteManager, self.eventController, perlin)
        self.entityManager.createHenchmenEntities(worldInitialFrame, self.phyManager)
        track = perlin.getSplineTrack()
        self.entityManager.onLoadPage(worldInitialFrame, perlin.getSplineTrack())
        
              
        self.skyManager = SkyManager(self.sceneManager, renderWindow.getViewport(0))
        self.renderWindow.addListener(self.skyManager.caelumSystem)
        ogre.Root.getSingletonPtr().addFrameListener(self.skyManager.caelumSystem)
        
        unit_z = ogre.Vector3(0.0, 0.0, 1.0)
        ogre.MeshManager.getSingleton().createPlane("Ground", ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME
                                                             , ogre.Plane(ogre.Vector3(0.0, 1.0, 0.0), 0.0), 100000, 100000, 1, 1, True, 1, 500.0, 500.0, unit_z)
        groundEnt = self.sceneManager.createEntity("GroundEntity", "Ground")
        #groundEnt.setMaterialName("BTFX/Wireframe")
        
        material = ogre.MaterialManager.getSingleton().getByName("GridNormalMap")
        material.setReceiveShadows(True)
        
        groundEnt.setMaterialName("GridNormalMap")
        groundEnt.setCastShadows(False)
        self.sceneManager.getRootSceneNode().createChildSceneNode().attachObject(groundEnt)
        #self.cameraNode.attachObject(groundEnt)
        
       
        
        node = self.sceneManager.getRootSceneNode().createChildSceneNode()
        node.attachObject(buildingEnt)
    
    def __del__(self):
        print "Delete"    
        #ogre.FrameListener.__del__(self)
    
    def _moveCamera(self):
        return None    
    
    def setupInput(self):
        self._setupInput()
        return
    
    def _setupInput(self):
        import platform
        int64 = False
        for bit in platform.architecture():
            if '64' in bit:
                int64 = True
        if int64:
            windowHnd = self.renderWindow.getCustomAttributeUnsignedLong("WINDOW")
        else:
            windowHnd = self.renderWindow.getCustomAttributeInt("WINDOW")

         #
         # Here is where we create the OIS input system using a helper function that takes python list of tuples
         #            
        #t= self._inputSystemParameters()
        params = [("WINDOW",str(windowHnd))]
        #params.extend(t)   
        self.InputManager = OIS.createPythonInputSystem( params )
        
        
        
        # Initialize OIS.
        #windowHnd = self.renderWindow.getCustomAttributeInt("WINDOW")
        #self.InputManager = OIS.createPythonInputSystem([("WINDOW", str(windowHnd))])
 
        # Create all devices, only catch joystick exceptions since most people use Key/Mouse.
        self.Keyboard = self.InputManager.createInputObjectKeyboard(OIS.OISKeyboard, True)
        self.Keyboard.setEventCallback(self)
        
        #self.Keyboard = self.InputManager.createInputObjectKeyboard(OIS.OISKeyboard,True)
        self.Mouse = self.InputManager.createInputObjectMouse(OIS.OISMouse, True)
        
        try:
            self.Joy = self.InputManager.createInputObjectJoyStick(OIS.OISJoyStick, self.bufferedJoy)
        except:
            self.Joy = False
 
        #Set initial mouse clipping size.
        self.windowResized(self.renderWindow)
        
        # Register as a Window listener.
        ogre.WindowEventUtilities.addWindowEventListener(self.renderWindow, self);
    
    def windowResized (self, rw):
         dummyint = 0
         width, height, depth, left, top= rw.getMetrics(dummyint,dummyint,dummyint, dummyint, dummyint)  # Note the wrapped function as default needs unsigned int's
         ms = self.Mouse.getMouseState()
         ms.width = width
         ms.height = height
         
    def windowClosed(self, rw):
      #Only close for window that created OIS (mWindow)
      if( rw == self.renderWindow ):
         if( self.InputManager ):
            self.InputManager.destroyInputObjectMouse( self.Mouse )
            self.InputManager.destroyInputObjectKeyboard( self.Keyboard )
            if self.Joy:
                self.InputManager.destroyInputObjectJoyStick( self.Joy )
            OIS.InputManager.destroyInputSystem(self.InputManager)
            self.InputManager=None
        #frameStarted
    def frameRenderingQueued(self, frameEvent):
        
        self.Keyboard.capture()
        self.Mouse.capture()
        
        if(self.renderWindow.isClosed()):
            return False
        
        self.timer.reset()
              
        #self.camera.moveRelative(self.direction * frameEvent.timeSinceLastFrame)       
        self.entityManager.update(frameEvent.timeSinceLastFrame, self.worldController)
        #self.phyManager.update(frameEvent.timeSinceLastFrame)
        self.worldController.update(frameEvent.timeSinceLastFrame)
        
        
        stats = self.doc.getElementByName("stats")
        backstory = self.doc.getElementByName("backstory")
        stats.setText("HELLO WORLD!")
        #pulser = self.doc.getElementByName("pulser")
        #pulser.addTime(int(frameEvent.timeSinceLastFrame * 1000))
        
        self.doc.update()
        
        return self.keepRendering
        
    def keyPressed(self, evt):
        if evt.key == OIS.KC_ESCAPE:
            self.keepRendering = False
            return
        
        if evt.key == OIS.KC_W:
            self.entityManager.advancePlayerTest(ogre.Vector3(0.0, 0.0, -1.0))
        if evt.key == OIS.KC_S:
            self.entityManager.advancePlayerTest(ogre.Vector3(0.0, 0.0, 1.0))
        return
    def keyReleased(self, evt):
        return
    
    def showDebugOverlay(self, show):
        """Turns the debug overlay (frame statistics) on or off."""
        overlay = ogre.OverlayManager.getSingleton().getByName('POCore/DebugOverlay')
        if overlay is None:
            self.statisticsOn = False
            ogre.LogManager.getSingleton().logMessage( "ERROR in sf_OIS.py: Could not find overlay POCore/DebugOverlay" )
            return
        if show:
            overlay.show()
        else:
            overlay.hide()
    
class TutorialApplication(sf.Application):
    
    def __init__(self):
        sf.Application.__init__(self)
        
    def _chooseSceneManager(self):
        self.sceneManager = self.root.createSceneManager(ogre.ST_EXTERIOR_CLOSE)
        
    def _createScene(self):
        
        sm = self.sceneManager
        sm.ambientLight = 0.25, 0.25, 0.25
        
        sm.setShadowUseInfiniteFarPlane(True)
        
        #lights
        """
        light = sm.createLight("Light1")
        #light.type = ogre.Light.LT_DIRECTIONAL
        dir = ogre.Vector3(1.0, -1.0, 0.2)
        dir.normalise()
        light.type = ogre.Light.LT_DIRECTIONAL
        #light.position = 250, 1000, 0
        light.direction = dir
        light.diffuseColour = 1, 1, 1
        light.specularColour = 1, 1, 1
        light.setCastShadows(True)
        """
        self.camera.setNearClipDistance(1.0)
        self.camera.setFarClipDistance(0)      
        cm = ogre.CompositorManager.getSingleton()
        cm.addCompositor(self.viewport, "Bloom")
        #cm.addCompositor(self.viewport, "HDR")
        #cm.addCompositor(self.viewport, "Gaussian Blur")
        cm.setCompositorEnabled(self.viewport, "Bloom", True) 
        
        #self.doc = canvas.Document(self.sceneManager, self.viewport, "DemoLayout.txt")
        
        #cm.registerCompositorLogic("HDR", HDRLogic())
        #cm.addCompositor(self.viewport, "HDR", 0)
        
        #cm.setCompositorEnabled(self.viewport, "HDR", True)
        #setup sprites
        #self.sceneManager.createBillboardSet()
        
        #create a temp group
        #self.sceneManager.setShadowTechnique(ogre.SHADOWTYPE_STENCIL_ADDITIVE)
        #self.sceneManager.setShadowTechnique(ogre.SHADOWTYPE_NONE)
        self.sceneManager.setShadowTechnique(ogre.SHADOWTYPE_TEXTURE_ADDITIVE)
    
    def _createCamera(self):
        self.camera = self.sceneManager.createCamera("PlayerCam")
        self.camera.nearClipDistance = 2
 
    def _createFrameListener(self):
        
        
        
        self.frameListener = TutorialFrameListener(self.renderWindow,
            self.camera,
            self.sceneManager
            , self
            )
        
        self.root.addFrameListener(self.frameListener)
        
        #self.frameListener.showDebugOverlay(True)


"""
class SpriteManager:
        
    @staticmethod    
    def constructSprites(window, batch, layers): 
        neiSpriteSheet = pyglet.image.load('data/sprites_nei_trans.png')
        rolfSpriteSheet = pyglet.image.load('data/sprites_rolf_trans.png')
        neiSequence = pyglet.image.ImageGrid(neiSpriteSheet.get_region(0, 2 * 55, 55, 2 * 55), 2, 1, item_width=54, item_height=54)
        rolfSequence = pyglet.image.ImageGrid(rolfSpriteSheet.get_region(0, rolfSpriteSheet.height - 55, 65 * 3, 55), 1, 3, item_width=65, item_height=52)
        sprites = []
        regionWidth = window.width / 4.0    
        padding = window.width / 8.0 
        for i in range(0, 4):
            
            if i % 2 == 0:
                neiSprite = AnimatedSprite(pyglet.image.Animation.from_image_sequence(rolfSequence.get_texture_sequence()[0:3], random.uniform(0.1, 1.0), True), batch=batch, group=layers[1])
                animFrames = [0, 1, 2]
                random.shuffle(animFrames)
                neiSprite.add_lookup(animFrames)
            else:
                neiSprite = AnimatedSprite(pyglet.image.Animation.from_image_sequence(neiSequence.get_texture_sequence()[0:2], random.uniform(0.1, 1.0), True), batch=batch, group=layers[1])
                animFrames = [0, 1]
                random.shuffle(animFrames)
                neiSprite.add_lookup(animFrames)
                
            #neiSprite.add_lookup([random.randint(0, 2),random.randint(0, 2), random.randint(0, 2)])
            neiSprite.set_active_lookup(1)
            neiSprite.x = padding + i * regionWidth - (neiSprite.width / 2.0 + 15.0)
            #neiSprite.y = -3
            neiSprite.scale = 2.0
            sprites.append(neiSprite)
        
        return sprites
    @staticmethod
    def constructEnemySprites(window, batch, layers, sprites):
        
        eyesoreSpriteSheet = pyglet.image.load('data/eyesore.png')
        sequence = pyglet.image.ImageGrid(eyesoreSpriteSheet.get_region(0, eyesoreSpriteSheet.height - 96, 102 * 3, 96), 1, 3, item_width=102, item_height=96, column_padding=1)
        regionWidth = window.width / 2
        for i in range(0, 1):
            sprite = AnimatedSprite(pyglet.image.Animation.from_image_sequence(sequence.get_texture_sequence()[0:3], 1, True), batch=batch, group=layers[0])
            animFrames = [0, 1]
            random.shuffle(animFrames)
            sprite.add_lookup(animFrames)
            sprite.set_active_lookup(1)
            sprite.x = regionWidth - (sprite.width / 2.0 + 15.0)
            sprite.y = window.height - sprite.height - sprite.height / 2.0 - 45.0
            sprite.scale = 2.0
            sprites.append(sprite)


class CharacterManager:
    def __init__(self):
        self.sprites = []
"""        